﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

using LowLevelGraphics.Filter;
using LowLevelGraphics.Drawing.FloodFillModes;

using Direction = LowLevelGraphics.Filter.Contours.Direction;

namespace LowLevelGraphics.Drawing
{
    /// <summary>
    /// This is class is used to be able to do some base operations at an UnsafeBitmap
    /// </summary>
    public class UnsafeGraphics
    {
        protected UnsafeBitmap m_Bitmap = null;
        protected Bresenham m_Bresenham = new Bresenham();

        /// <summary>
        /// Initializes a new instance of the <see cref="UnsafeGraphics"/> class.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        public UnsafeGraphics(UnsafeBitmap _bitmap)
        {
            m_Bitmap = _bitmap;
        }

        /// <summary>
        /// Draws the circle.
        /// </summary>
        /// <param name="_pen">The _pen.</param>
        /// <param name="_rectangle">The _rectangle.</param>
        public void DrawCircle(Pen _pen, Rectangle _rectangle)
        {
            m_Bresenham.RasterCircle(m_Bitmap, _rectangle.X, _rectangle.Y, _rectangle.Width, _pen.Color);
        }

        /// <summary>
        /// Draws the lines.
        /// </summary>
        /// <param name="_pen">The _pen.</param>
        /// <param name="_aPoint">The _a point.</param>
        public void DrawLines(Pen _pen, Point[] _aPoint)
        {
            Point ptStart = _aPoint[0];
            for (int i = 1; i < _aPoint.Length; i++)
            {
                Point ptPrev = _aPoint[i-1];
                Point ptCurrent = _aPoint[i];
                DrawLine(_pen, ptPrev, ptCurrent);
            }
        }

        /// <summary>
        /// Draws a point
        /// </summary>
        /// <param name="_pen"></param>
        /// <param name="_nX"></param>
        /// <param name="_nY"></param>
        public void DrawPoint(Pen _pen, int _nX, int _nY)
        {
            m_Bitmap.SetPixel(_nX, _nY, _pen.Color);
        }

        /// <summary>
        /// Draws the contour paths
        /// </summary>
        /// <param name="_pen"></param>
        /// <param name="?"></param>
        public void DrawContourPath(Pen _pen, Point _ptStart, IEnumerable<Direction> _aDirection)
        {
            int x = _ptStart.X;
            int y = _ptStart.Y;

            Color color = _pen.Color;
            foreach (Direction direction in _aDirection)
            {
                IncreaseDirectionDependant(ref x, ref y, direction);
                m_Bitmap.SetPixel(x, y, color);
            }

        }

        public static void IncreaseDirectionDependant(ref int x, ref int y, Direction direction)
        {
            if (direction == Direction.N)
            {
                y--;
            }
            else if (direction == Direction.S)
            {
                y++;
            }
            else if (direction == Direction.W)
            {
                x--;
            }
            else if (direction == Direction.E)
            {
                x++;
            }
            else if (direction == Direction.NE)
            {
                x++;
                y--;
            }
            else if (direction == Direction.NW)
            {
                x--;
                y--;
            }
            else if (direction == Direction.SE)
            {
                x++;
                y++;
            }
            else if (direction == Direction.SW)
            {
                x--;
                y++;
            }
        }

        /// <summary>
        /// Froms the image.
        /// </summary>
        /// <param name="_unsafeBitmap">The _unsafe bitmap.</param>
        /// <returns></returns>
        public static UnsafeGraphics FromImage(UnsafeBitmap _unsafeBitmap)
        {
            return new UnsafeGraphics(_unsafeBitmap);
        }

        ///// <summary>
        ///// Draws to.
        ///// </summary>
        //public void DrawTo()
        //{
        //}

        /// <summary>
        /// Draws the line.
        /// </summary>
        /// <param name="_pen">The _pen.</param>
        /// <param name="nX1">The n x1.</param>
        /// <param name="nY1">The n y1.</param>
        /// <param name="nX2">The n x2.</param>
        /// <param name="nY2">The n y2.</param>
        public void DrawLine(Pen _pen, int nX1, int nY1, int nX2, int nY2)
        {
            DrawLine(_pen, new Point(nX1, nY1), new Point(nX2, nY2));
        }

        /// <summary>
        /// Draws the line.
        /// </summary>
        /// <param name="_pen">The _pen.</param>
        /// <param name="_ptStart">The _PT start.</param>
        /// <param name="_ptEnd">The _PT end.</param>
        public void DrawLine(Pen _pen, Point _ptStart, Point _ptEnd)
        {
            IEnumerable<Point> aLineElement = Bresenham.RenderLine(_ptStart, _ptEnd);
            foreach (Point ptPoint in aLineElement)
            {
                m_Bitmap.SetPixel(ptPoint.X, ptPoint.Y, _pen.Color);
            }
        }

        /// <summary>
        /// Sets a point to given color
        /// </summary>
        /// <param name="_pen"></param>
        /// <param name="_ptPoint"></param>
        public void SetPoint(Pen _pen, Point _ptPoint)
        {
            m_Bitmap.SetPixel(_ptPoint.X, _ptPoint.Y, _pen.Color); 
        }

        /// <summary>
        /// Sets a point to given color
        /// </summary>
        /// <param name="_color"></param>
        /// <param name="_nX"></param>
        /// <param name="_nY"></param>
        public void SetPoint(Color _color, int _nX, int _nY)
        {
            m_Bitmap.SetPixel(_nX, _nY, _color);
        }

        /// <summary>
        /// Sets a point to given Color
        /// </summary>
        /// <param name="_pen"></param>
        /// <param name="_nX"></param>
        /// <param name="_nY"></param>
        public void SetPoint(Pen _pen, int _nX, int _nY)
        {
            m_Bitmap.SetPixel(_nX, _nY, _pen.Color);
        }

        /// <summary>
        /// Fills the rectangle.
        /// </summary>
        /// <param name="_color">The _color.</param>
        /// <param name="_rectangle">The _rectangle.</param>
        public void FillRectangle(Color _color, Rectangle _rectangle)
        {
            FillRectangle(new SolidBrush(_color), _rectangle);
        }

        /// <summary>
        /// Fills the rectangle.
        /// </summary>
        public void FillRectangle(SolidBrush _brush, Rectangle _rectangle)
        {
            for (int y = _rectangle.Top; y <= _rectangle.Bottom; y++)
            {
                for (int x = _rectangle.Left; x <= _rectangle.Right; x++)
                {
                    m_Bitmap.SetPixel(x, y, _brush.Color);
                }
            }
        }

        /// <summary>
        /// Fills the rectangle.
        /// </summary>
        /// <param name="_brush">The _brush.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        public void FillRectangle(SolidBrush _brush, int x, int y, int width, int height)
        {
            FillRectangle(_brush, new Rectangle(x, y, width, height));
        }

        /// <summary>
        /// Fills the rectangles.
        /// </summary>
        /// <param name="brush">The brush.</param>
        /// <param name="_aRectangle">The _a rectangle.</param>
        public void FillRectangles(SolidBrush brush, Rectangle[] _aRectangle)
        {
            foreach (Rectangle rectangle in _aRectangle)
            {
                DrawRectangle(new Pen(brush.Color), rectangle);
            }
        }

        /// <summary>
        /// Draws the rectangle.
        /// </summary>
        /// <param name="pen">The pen.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        public void DrawRectangle(Pen pen, int x, int y, int width, int height)
        {
            DrawRectangle(pen, new Rectangle(x, y, width, height));
        }


        /// <summary>
        /// Draws the rectangle.
        /// </summary>
        /// <param name="pen">The pen.</param>
        /// <param name="_rectangle">The _rectangle.</param>
        public void DrawRectangle(Pen pen, Rectangle _rectangle)
        {
            DrawLines(pen, 
                new Point[]{_rectangle.Location, 
                new Point(_rectangle.Right, _rectangle.Top), 
                new Point(_rectangle.Right, _rectangle.Bottom), 
                new Point(_rectangle.Left, _rectangle.Bottom),
                _rectangle.Location});
        }

        /// <summary>
        /// Floods the fill.
        /// </summary>
        /// <param name="_ptPoint">The _PT point.</param>
        /// <param name="_color">The _color.</param>
        /// <param name="_abstractFloodFill">The _abstract flood fill.</param>
        public void FloodFill(Point _ptPoint, Color _color, AbstractFloodFill _abstractFloodFill)
        {
            _abstractFloodFill.Fill(m_Bitmap, _ptPoint.X, _ptPoint.Y, _color);
        }

        /// <summary>
        /// Flood Fill operation.
        /// </summary>
        /// <param name="_ptPoint">The _PT point.</param>
        /// <param name="_color">The _color.</param>
        public void FloodFill(Point _ptPoint, Color _color)
        {
            FloodFill(_ptPoint, _color, new FloodFill3());
        }

        /// <summary>
        /// Fills the ellipse.
        /// </summary>
        /// <param name="_brush">The _brush.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="nLength1">The n length1.</param>
        /// <param name="nLength2">The n length2.</param>
        public void FillEllipse(Brush _brush, int x, int y, int nLength1, int nLength2)
        {
            //FillEllipse();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_brush"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="nLength"></param>
        public void FillCircle(SolidBrush _brush, int x, int y, int nLength)
        {
            m_Bresenham.RasterCircle(m_Bitmap, x, y, nLength, _brush.Color);
        }

        //public void DrawCircle(Pen _pen, Rectangle _rectangle)
        //{
        //    m_Bresenham.RasterCircle(m_Bitmap, _rectangle.X, _rectangle.Y, nLength, _brush.Color);
        //}

        /// <summary>
        /// Draws the circle.
        /// </summary>
        /// <param name="_pen">The _pen.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        public void DrawCircle(Pen _pen, int x, int y, int width, int height)
        {
            DrawCircle(_pen, new Rectangle(x, y, width, height));
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        public void Dispose()
        {
        }
    }
}
